package Cliente;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;

import javax.swing.JOptionPane;

import jogoKing.EstadoJogo;
import jogoKing.leilao.JogadorOferta;
import jogoKing.leilao.RespostasLeilao;

import Cliente.interface_grafico.JogadoresJogoFechado;
import baralho.carta.Carta;
import baralho.carta.Naipe;

import mensagem.JogadorJogou;
import mensagem.MEntraJogo;
import mensagem.MLeilao;
import mensagem.MListaJogadores;
import mensagem.MObject;
import mensagem.MObjectArray;
import mensagem.MOferta;
import mensagem.MPontuacao;
import mensagem.MSimples;
import mensagem.MVencedorRonda;
import mensagem.TipoMensagem;
import mensagem.TipoMiniJogo;

public class Comunicacao implements Runnable {

	private final int port = 5000;
	private Socket socket = null;
	private ObjectOutputStream output;
	private ObjectInputStream input;
	private int id;
	private String nickname;
	private boolean espera_input = true;
	private JogoKingCliente cliente_king;
	private LigacaoJogoCliente ligacao_jogo;
	private Decisao decisao;

	public Comunicacao(ObjectInputStream in, ObjectOutputStream out,
			Decisao modulo_decisao) {

		this.output = out;
		this.input = in;
		this.decisao = modulo_decisao;

		// Registar o encerramento dos Streams como ultima accao do programa
		// antes da terminacao.
		 Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public
		 void run(){
		 
		 try { synchronized(output){ System.out.println("Jogador vai sair");
		 output.writeObject(new MObject<Integer>(TipoMensagem.CLIENTE_LOGOFF,
		 decisao.getId())); output.flush(); output.close(); }
		 synchronized(input){
		 
		 input.close(); System.out.println("Fecha input"); socket.close(); } }
		 catch (IOException ioe) { } } } );
	}

	@Override
	public void run() {

		this.decisao.correLogin();

		// Esperar por Mensagens
		// ..continuamente, ate ordem em contrario (vindo do Utilizador ou do
		// Servidor)
		while (espera_input) {

			trataMensagem(recebeMensagem()); // Analisa a Mensagem
		}

	}

	/**
	 * Faz a recep��o de todas as Mensagens enviadas pelo Servidor
	 * 
	 * @return MSimples recebida
	 */
	public MSimples recebeMensagem() {
		MSimples mensagem = null;
		try {
			mensagem = (MSimples) this.input.readObject();
			System.out.println("Recebi uma mensagem! "
					+ mensagem.getTipo().toString());
		} catch (SocketException e) {
			System.exit(0);
		} catch (EOFException e) {
			// e.printStackTrace();
			// System.exit(0);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return mensagem;
	}

	/**
	 * Verifica Tipo da Mensagem e executa as instru��es definidas para esse
	 * Tipo
	 * 
	 * @param mensagem:
	 *            MSimples
	 */
	public void trataMensagem(MSimples mensagem) {

		/*
		 * if (mensagem == null) return;
		 * System.out.println(mensagem.getTipo().toString());
		 */

		TipoMensagem tipo_mensagem = mensagem.getTipo();
		if (tipo_mensagem.equals(TipoMensagem.SERVIDOR_JOGO)) {
			this.ligacao_jogo = new LigacaoJogoCliente();
			cliente_king = new JogoKingCliente(
					this.ligacao_jogo, ((MEntraJogo) mensagem).getIdJogo(),
					((MEntraJogo) mensagem).getNomes(), this.decisao);
			cliente_king.start();
			return;
		}else if(tipo_mensagem.equals(TipoMensagem.SERVIDOR_JOGADORESJOGOFECHADO)){
			decisao.novoJogoFechado(mensagem);
		} else if (tipo_mensagem.equals(TipoMensagem.SERVIDOR_ESTADO_JOGO)) {
			regressoJogo(mensagem);

		}

		// Se for carta ou string da Mesa, envia para a Thread que trata do Jogo
		else if ((tipo_mensagem.equals(TipoMensagem.SERVIDOR_MAO))
				|| (tipo_mensagem.equals(TipoMensagem.SERVIDOR_JOGADOR_ACTUAL))
				|| (tipo_mensagem.equals(TipoMensagem.SERVIDOR_MESA))
				|| (tipo_mensagem.equals(TipoMensagem.SERVIDOR_PEDECARTA))
				|| (tipo_mensagem.equals(TipoMensagem.SERVIDOR_VENCEDORRONDA))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_FIM_MINIJOGO))
				|| (mensagem.getTipo().equals(TipoMensagem.TipoMiniJogo))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_PONTOS))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_HA_LEILAO))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_INFORMA))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_ESCOLHE_NEG_POS))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_AUMENTA_OFERTA))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_ACEITA_OFERTA))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_4_3_3))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_OFERTA_ACTUAL))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_OFERTANTE))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_DONO))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_LEILAO_FIM))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_LEILAO_ESCOLHETRUNFO))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_FIMJOGO))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_TRUNFO))) {
			this.ligacao_jogo.setMensagem(mensagem);

			return;
		} else if (mensagem.getTipo().equals(TipoMensagem.BD_OK)) {
			this.decisao.informa("Os dados foram actualizados!");
			return;
		} else if (mensagem.getTipo().equals(TipoMensagem.BD_ERRO)) {
			this.decisao.erro("Houve um erro ao aceder � base de dados!");
			return;
		} else if ((mensagem.getTipo().equals(TipoMensagem.SERVIDOR_PERFIL))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_PESQUISA))
				|| (mensagem.getTipo().equals(TipoMensagem.SERVIDOR_TOP))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_ESTATISTICA))
				|| (mensagem.getTipo()
						.equals(TipoMensagem.SERVIDOR_PROCURA_JOGADOR))) {
			this.decisao.mostraEstatistica(mensagem, this.output);
			return;
		} else if (mensagem.getTipo().equals(TipoMensagem.MENSAGEM_ERRO)) {
			this.decisao
					.erro("Houve um erro na comunica��o com o Servidor!");
			return;
//		}else if(){
//			
//		}
		
		}else if (mensagem.getTipo().equals(
						TipoMensagem.SERVIDOR_JOGOS_ABERTOS))
			this.decisao.jogosAbertos(mensagem, this.output);

	}

	private void regressoJogo(MSimples mensagem) {
		EstadoJogo estado_jogo = ((MObject<EstadoJogo>) mensagem).getObject();

		this.ligacao_jogo = new LigacaoJogoCliente();
		cliente_king = new JogoKingCliente(
				this.ligacao_jogo, estado_jogo.getIdJogo(), estado_jogo
						.getOponentes(), this.decisao);

		this.decisao.regressoJogo(estado_jogo.getEstadoMesa(), estado_jogo
				.getJogadorActual(), estado_jogo.getMao(), estado_jogo
				.getNumeroCartasMaos(), estado_jogo.getTipoMiniJogo());

		cliente_king.start();
		return;

	}

	/**
	 * Classe do Cliente encarregue de todas as opera��es relativas ao
	 * JogoKing
	 * 
	 */
	class JogoKingCliente extends Thread {

		private LigacaoJogoCliente ligacao_jogo;
		private int id_jogo;
		private boolean recebe_mensagens = true; // Provis�rio
		private Decisao decisao;

		/**
		 * 
		 * @param nickname:
		 *            Nickname do Jogador
		 * @param nomes:
		 *            Nicknames dos Jogadores presentes no Jogo actual
		 * @param output:
		 *            Canal de output para o Servidor
		 * @param ligacao_jogo:
		 *            Objecto partilhado para troca de Mensagens entre a
		 *            Classe-M�e do Jogador e a Classe do JogoKing
		 * @param id_jogo:
		 *            Id do Jogo em que o Jogador est� a participar
		 */
		public JogoKingCliente(
				LigacaoJogoCliente ligacao_jogo, int id_jogo, String[] nomes,
				Decisao decisao) {
			this.id_jogo = id_jogo;
			
			this.ligacao_jogo = ligacao_jogo;
			
			this.decisao = decisao;
			this.decisao.iniciaJogo(this.id_jogo);
			this.decisao.organizaNomes(nomes);
			

		}

		public void run() {

			while (this.recebe_mensagens) {
				recebeMensagens();
			}

		}

		private void recebeMensagens() {
			TipoMensagem tipo = this.ligacao_jogo.getTipoMensagem();
			System.out.println("Tipo mensagem????? " + tipo.toString());
			if (tipo.equals(TipoMensagem.SERVIDOR_PEDECARTA)) {
				this.ligacao_jogo.retiraMensagem();
				this.decisao.jogaCarta();
				return;
			} else if (tipo.equals(TipoMensagem.SERVIDOR_VENCEDORRONDA)) {
				this.decisao.mostraVencedor((MVencedorRonda) this.ligacao_jogo
						.retiraMensagem());
				return;
			} else if (tipo.equals(TipoMensagem.SERVIDOR_FIM_MINIJOGO)) {
				this.decisao
						.terminouMiniJogo((MObject<Boolean>) this.ligacao_jogo
								.retiraMensagem());
				return;
			} else if (tipo.equals(TipoMensagem.SERVIDOR_MESA)) {
				this.decisao
						.actualizaMesa((MObject<JogadorJogou>) this.ligacao_jogo
								.retiraMensagem());
				return;
			} else if (tipo.equals(TipoMensagem.SERVIDOR_MAO)) {
				this.decisao.recebeMao(((MObjectArray<Carta>) this.ligacao_jogo
						.retiraMensagem()).getObject());
				return;
			} else if (tipo.equals(TipoMensagem.TipoMiniJogo)) {
				this.decisao
						.setMiniJogoActual(((MObject<TipoMiniJogo>) this.ligacao_jogo
								.retiraMensagem()).getObject());
				return;
			} else if (tipo.equals(TipoMensagem.SERVIDOR_PONTOS)) {
				MPontuacao mpontos = (MPontuacao) this.ligacao_jogo
						.retiraMensagem();
				this.decisao.actualizaPontos((String[]) mpontos.getNomes(),
						(int[]) mpontos.getPontuacoes(), (int[]) mpontos.getIds());
				return;
			} else if (tipo.equals(TipoMensagem.SERVIDOR_FIMJOGO)) {
				this.ligacao_jogo.retiraMensagem();
				this.recebe_mensagens = false;
				this.decisao.terminaJogo();
				// Nao pode ficar so assim
			} else if (tipo.equals(TipoMensagem.SERVIDOR_JOGADOR_ACTUAL)) {
				this.decisao
						.setJogadorActual(((MObject<String>) this.ligacao_jogo
								.retiraMensagem()).getObject());
			}else if (tipo.equals(TipoMensagem.SERVIDOR_TRUNFO)){
				this.decisao.trunfoPositivo(((MObject<Naipe>)this.ligacao_jogo.retiraMensagem()).getObject());
				
			} else if (tipo.equals(TipoMensagem.SERVIDOR_HA_LEILAO)) {
				this.ligacao_jogo.retiraMensagem();
				this.decisao.haLeilao();

			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_4_3_3)) {
				this.ligacao_jogo.retiraMensagem();
				this.decisao.leilao433();

			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_INFORMA)) {
				String mensagem = ((MObject<String>) this.ligacao_jogo
						.retiraMensagem()).getObject();
				this.decisao.informa(mensagem);
			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_ACEITA_OFERTA)) {
				MObject<Integer> mensagem = (MObject<Integer>) this.ligacao_jogo
						.retiraMensagem();
				this.decisao.aceitaOferta(mensagem);
			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_AUMENTA_OFERTA)) {
				/*
				 * this.ligacao_jogo.retiraMensagem(); boolean pode_jogar_igual =
				 * false;
				 * if(this.ligacao_jogo.getTipoMensagem().equals(TipoMensagem.SERVIDOR_LEILAO_IGUAL))
				 * pode_jogar_igual = true; this.ligacao_jogo.retiraMensagem();
				 */
				MOferta mensagem = (MOferta) this.ligacao_jogo.retiraMensagem();
				this.decisao.aumentaOferta(mensagem);
			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_DONO)) {
				String dono = ((MObject<String>) this.ligacao_jogo
						.retiraMensagem()).getObject();
				this.decisao.donoLeilao(dono);

			}

			else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_ESCOLHETRUNFO)) {
				this.ligacao_jogo.retiraMensagem();
				this.decisao.escolheTrunfo();
			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_FIM)) {
				this.ligacao_jogo.retiraMensagem();
				this.decisao.fimLeilao();
			} else if (tipo
					.equals(TipoMensagem.SERVIDOR_LEILAO_ESCOLHE_NEG_POS)) {
				this.ligacao_jogo.retiraMensagem();
				this.decisao.negativos();
			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_OFERTA_ACTUAL)) {
				MObject<JogadorOferta> mensagem = (MObject<JogadorOferta>) this.ligacao_jogo
						.retiraMensagem();
				this.decisao.ofertaActual(mensagem);
			} else if (tipo.equals(TipoMensagem.SERVIDOR_LEILAO_OFERTANTE)) {
				MObject<String> mensagem = (MObject<String>) this.ligacao_jogo
						.retiraMensagem();
				this.decisao.setOfertante(mensagem);
			} else if (tipo.equals(TipoMensagem.ENCERRAR_JOGO)) {
				MObject<String> mensagem = (MObject<String>) this.ligacao_jogo
						.retiraMensagem();
				this.decisao.informa(mensagem.getObject());
				this.decisao.terminaJogo();
				this.recebe_mensagens = false;
			} else {
				this.ligacao_jogo.retiraMensagem();
				//Nao faz nada!
			}

		}

	}
}
